package me.illtamer.infinite.file.loader;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 文件监听处理类
 * */
@Slf4j
public class FileAlterationManager {
    private final static ScheduledExecutorService scheduled = new ScheduledThreadPoolExecutor(1 << 3, new BasicThreadFactory.Builder().namingPattern("File-Alteration-Pool-%d")/*.daemon(true)*/.build());
    private static final Set<ScheduledFuture<?>> scheduledTasks = new HashSet<>();
    private final String pathStr;
    private final FileHandler handler;
    private WatchService watchService;

    private FileAlterationManager(String pathStr, FileHandler handler) {
        this.pathStr = pathStr;
        this.handler = handler;
        try {
            watchService = FileSystems.getDefault().newWatchService();
            ScheduledFuture<?> future = scheduled.scheduleAtFixedRate(new ListenerRunnable(), 2L, 2L, TimeUnit.SECONDS);
            scheduledTasks.add(future);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 注册监听事件
     * @param pathStr 监听路径
     * @param handler 处理类
     * @param events 监听事件
     * */
    public static void register(String pathStr, FileHandler handler, WatchEvent.Kind<?>... events) {
        if (pathStr == null || pathStr.length() == 0)
            throw new IllegalArgumentException(pathStr);
        Path path = Paths.get(pathStr);
        FileAlterationManager resourceListener = new FileAlterationManager(pathStr, handler);
        try {
            path.register(resourceListener.watchService, events);
        } catch (IOException e) {
            log.error("Error occurred when register {} !", handler, e);
        }
    }

    /**
     * 取消所有注册的监听
     * */
    public static void unregisterAll() {
        scheduledTasks.forEach(scheduledFuture -> scheduledFuture.cancel(true));
    }

    private class ListenerRunnable implements Runnable {
        public void run() {
            try {
                    WatchKey watchKey = watchService.take();
                    List<WatchEvent<?>> watchEvents = watchKey.pollEvents();
                    for (WatchEvent<?> event : watchEvents) {
                        if (event.context() == null || !(event.context() instanceof Path)) {
                            log.debug("Unexpected event context {}", event.context());
                            continue;
                        }
                        handler.handle(event.kind(), new File(pathStr, event.context().toString()));
                    }
                    watchKey.reset();
            } catch (Exception e) {
                log.error("Error occurred when handler-{} handler event !", handler, e);
            }
        }
    }

}


